/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */
package xowl.gmi.controller;

import java.util.Collection;

/**
 *
 * @author Laurent WOUTERS
 */
public class GraphConnector extends ASTElement implements xowl.gmi.view.diagram.GraphConnector.Actions {
    protected xowl.gmi.model.cvssl.Connector definition;
    protected xowl.gmi.view.diagram.GraphConnector widget;
    protected Element legend;
    protected Element legendLeft;
    protected Element legendRight;
    protected GraphNode origin;
    protected GraphNode target;
    protected java.awt.Color borderColor;
    protected int borderThickness;
    protected Integer obsOriginPort;
    protected Integer obsTargetPort;
    
    public xowl.gmi.view.diagram.GraphConnector getVisual() { return widget; }
    public GraphNode getOrigin() { return origin; }
    public GraphNode getTarget() { return target; }
    
    public GraphConnector(Controller controller, Element parent, xowl.gmi.model.ast.ASTNode ast) {
        super(controller, parent, ast);
        this.definition = (xowl.gmi.model.cvssl.Connector)ast.getSymbol();
        borderColor = getValueColor(definition.getBorderColor(), getObserverBorderColor());
        Integer thickness = getValueInteger(definition.getBorderThickness(), getObserverBorderThickness());
        if (thickness == null) borderThickness = 1;
        else borderThickness = thickness;
        xowl.gmi.model.ast.Decorator decorator = this.getDecorator();
        decorator.addPropertyObserver("originPort", new xowl.interpreter.adapter.DataPropertyObserver() {
            @Override public void onAdd(Object elem) {
                obsOriginPort = (Integer)elem;
                onPortsSet();
            }
            @Override public void onRemove(Object elem) { }
        });
        decorator.addPropertyObserver("targetPort", new xowl.interpreter.adapter.DataPropertyObserver() {
            @Override public void onAdd(Object elem) {
                obsTargetPort = (Integer)elem;
                onPortsSet();
            }
            @Override public void onRemove(Object elem) { }
        });
        decorator.addPropertyObserver("style", new xowl.interpreter.adapter.ObjectPropertyObserver() {
            @Override public void onAdd(xowl.interpreter.adapter.ProxyObject elem) {
                widget.setStyle(getConnectorStyle((xowl.gmi.model.ast.ConnectorStyle)elem));
            }
            @Override public void onRemove(xowl.interpreter.adapter.ProxyObject elem) { }
        });
        
        xowl.gmi.view.diagram.GraphConnectorEnd end1 = getEnd(definition.getOrigin());
        xowl.gmi.view.diagram.GraphConnectorEnd end2 = getEnd(definition.getTarget());
        this.widget = new xowl.gmi.view.diagram.GraphConnector(this, controller.getLocale(), end1, end2, borderColor, borderThickness);
        setupExecution(widget);
    }
    
    public void setLegend(Element legend) {
        this.legend = legend;
        this.widget.setChild(this.legend.getWidget());
    }
    
    public void setNodes(GraphNode origin, GraphNode target) {
        this.origin = origin;
        this.target = target;
        this.widget.setNodes(origin.getVisual(), target.getVisual());
    }
    
    private void onPortsSet() {
        if (obsOriginPort != null && obsTargetPort != null) {
            widget.setPorts(obsOriginPort, obsTargetPort);
            obsOriginPort = null;
            obsTargetPort = null;
            controller.onDiagramUpdated();
        }
    }
    
    private xowl.interpreter.adapter.DataPropertyObserver getObserverBorderColor() {
        return new xowl.interpreter.adapter.DataPropertyObserver() {
            @Override public void onAdd(Object elem) {
                widget.setBorderColor(new java.awt.Color((Integer)elem));
                controller.onDiagramUpdated();
            }
            @Override public void onRemove(Object elem) { }
        };
    }
    private xowl.interpreter.adapter.DataPropertyObserver getObserverBorderThickness() {
        return new xowl.interpreter.adapter.DataPropertyObserver() {
            @Override public void onAdd(Object elem) {
                widget.setBorderThickness((Integer)elem);
                controller.onDiagramUpdated();
            }
            @Override public void onRemove(Object elem) { }
        };
    }
    
    @Override protected void onChildAdd(xowl.gmi.model.ast.ASTNode node) { legend.insertAST(node); }
    @Override protected void onChildRemove(xowl.gmi.model.ast.ASTNode node) { legend.removeAST(node); }
    
    @Override public xowl.gmi.view.diagram.Widget getWidget() { return widget; }
    
    @Override public void saveTo(xowl.gmi.model.Diagram diagram) {
        xowl.lang.owl2.IRI modelIRI = this.getModelIRI();
        if (modelIRI != null) {
            String iri = modelIRI.getHasValue();
            String piri = this.getASTParent().getModelIRI().getHasValue();
            xowl.gmi.model.ast.Decorator decorator = getDecorator();
            diagram.addElement(new xowl.gmi.model.DiagramRelation(piri, iri, decorator.getOriginPort(), decorator.getTargetPort()));
        } else {
            String piri = this.getASTParent().getModelIRI().getHasValue();
            String left = origin.getModelIRI().getHasValue();
            String right = target.getModelIRI().getHasValue();
            String property = this.getPropertyIRI().getHasValue();
            xowl.gmi.model.ast.Decorator decorator = getDecorator();
            diagram.addElement(new xowl.gmi.model.DiagramLink(piri, left, right, property, decorator.getOriginPort(), decorator.getTargetPort()));
        }
        if (legend != null)
            legend.saveTo(diagram);
    }
    @Override public void decommission() {
        this.decommissionListeners();
        if (legend != null)
            legend.decommission();
    }
    
    @Override public void onMenuStyleLine() { actionSetStyle(xowl.gmi.model.ast.ConnectorStyle.get_line(controller.getRepository())); }
    @Override public void onMenuStylePolyline() { actionSetStyle(xowl.gmi.model.ast.ConnectorStyle.get_polyline(controller.getRepository())); }
    @Override public void onMenuStyleCurve() { actionSetStyle(xowl.gmi.model.ast.ConnectorStyle.get_curve(controller.getRepository())); }
    
    public void actionSetStyle(xowl.gmi.model.ast.ConnectorStyle newStyle) {
        String parentNode = getASTParent().getASTNode().getIRIString();
        String childNode = astNode.getIRIString();
        xowl.gmi.model.ast.ConnectorStyle oldStyle = getDecorator().getStyle();
        NodeAction action = new GraphConnectorSetStyle(controller.getRepository(), parentNode, childNode, oldStyle, newStyle);
        DiagramUIAction wrapper = new DiagramUIAction(controller.getLocale().diagMoveElem);
        wrapper.addInner(action);
        controller.executeUIAction(wrapper);
    }
    
    private xowl.gmi.view.diagram.GraphConnectorEnd getEnd(xowl.gmi.model.cvssl.ConnectorEnd end) {
        switch (getEndStyle(end.getStyle())) {
            case arrow:
                return new xowl.gmi.view.diagram.GraphConnectorEndArrow(borderColor, borderThickness);
            case diamond:
                return new xowl.gmi.view.diagram.GraphConnectorEndDiamond(borderColor, borderThickness);
        }
        return new xowl.gmi.view.diagram.GraphConnectorEndLink();
    }
    
    private GMIConnectorEndStyle getEndStyle(xowl.gmi.model.cvssl.ConnectorEndStyle style) {
        if (style == null)
            return GMIConnectorEndStyle.link;
        String[] parts = style.getIRIString().split("#");
        String iri = parts[parts.length-1];
        if (iri.equalsIgnoreCase("link"))
            return GMIConnectorEndStyle.link;
        if (iri.equalsIgnoreCase("arrow"))
            return GMIConnectorEndStyle.arrow;
        if (iri.equalsIgnoreCase("diamond"))
            return GMIConnectorEndStyle.diamond;
        if (iri.equalsIgnoreCase("circle"))
            return GMIConnectorEndStyle.circle;
        return GMIConnectorEndStyle.link;
    }
    
    public xowl.gmi.view.diagram.GraphConnectorStyle getConnectorStyle(xowl.gmi.model.ast.ConnectorStyle cstyle) {
        if (cstyle == null)
            return xowl.gmi.view.diagram.GraphConnectorStyle.Line;
        String[] parts = cstyle.getIRIString().split("#");
        String iri = parts[parts.length-1];
        if (iri.equalsIgnoreCase("line"))
            return xowl.gmi.view.diagram.GraphConnectorStyle.Line;
        if (iri.equalsIgnoreCase("polyline"))
            return xowl.gmi.view.diagram.GraphConnectorStyle.Polyline;
        if (iri.equalsIgnoreCase("curve"))
            return xowl.gmi.view.diagram.GraphConnectorStyle.Curve;
        return xowl.gmi.view.diagram.GraphConnectorStyle.Line;
    }
    
    protected xowl.lang.owl2.IRI getPropertyIRI() {
        xowl.lang.actions.QueryVariable varT = new xowl.lang.actions.QueryVariable();
        varT.setName("t");
        xowl.lang.actions.QueryVariable varP = new xowl.lang.actions.QueryVariable();
        varP.setName("p");
        
        xowl.lang.owl2.ObjectPropertyAssertion axiom1 = new xowl.lang.owl2.ObjectPropertyAssertion();
        axiom1.setObjectProperty(controller.getCommandBuilder().getMetaConnector().getHasIRI());
        axiom1.setIndividual(varT);
        axiom1.setValueIndividual(astNode.getIRI());
        
        xowl.lang.owl2.ObjectPropertyAssertion axiom2 = new xowl.lang.owl2.ObjectPropertyAssertion();
        axiom2.setObjectProperty(controller.getCommandBuilder().getMetaProperty().getHasIRI());
        axiom2.setIndividual(varT);
        axiom2.setValueIndividual(varP);
        
        xowl.lang.actions.ValueOf query = new xowl.lang.actions.ValueOf();
        query.addAxioms(axiom1);
        query.addAxioms(axiom2);
        query.setOn(varP);
        try {
            Collection<xowl.interpreter.QuerySolution> solutions = controller.getRepository().select(query);
            for (xowl.interpreter.QuerySolution solution : solutions)
                return ((xowl.lang.runtime.Entity)solution.getValue(varP)).getHasIRI();
        } catch (xowl.interpreter.XOWLException ex) { }
        return null;
    }
}
